<?php
#
# dmBridge: a data access framework for CONTENTdm(R)
#
# Copyright © 2009, 2010, 2011 Board of Regents of the Nevada System of Higher
# Education, on behalf of the University of Nevada, Las Vegas
#

/**
 * Encapsulates a session associated with an incoming HTTP request. Typically
 * this is populated with the contents of the $_SESSION superglobal upon
 * instantiation.
 *
 * @author Alex Dolski <alex.dolski@unlv.edu>
 * @license http://www.opensource.org/licenses/mit-license.php
 */
final class DMSession {

	/** @var Array of DMObjects */
	private $favorites = array();

	public function __construct() {
		$this->populateFavorites();
	}

	/**
	 * Saves a DMObject to the user's favorites. Must be called before the
	 * HTTP header has been output. Duplicate objects will not be saved.
	 *
	 * @return void
	 */
	public function addFavorite(DMObject $obj) {
		// check for duplicates
		foreach ($this->favorites as $fav) {
			if ($fav->equals($obj)) {
				return;
			}
		}

		if (!array_key_exists('BUF', $_COOKIE)) {
			$_COOKIE['BUF'] = "";
		}
		$buf = &$_COOKIE['BUF'];
		$objcookie = sprintf("%s<%d>",
				$obj->getCollection()->getAlias(),
				$obj->getPtr());
		if (strpos($buf, $objcookie) === false) {
			$buf .= $objcookie;
			if (!headers_sent()) {
				setcookie('BUF', $buf, null, '/');
			}
		}
		$this->favorites[] = $obj;
	}

	/**
	 * Returns an array of all DMObjects in the user's favorites that are
	 * accessible from within the given template set.
	 *
	 * @param DMTemplateSet The template set in which to search
	 * @return Array of DMObjects, or an empty array if none exist
	 * @see getObjects()
	 */
	public function getAccessibleFavorites(DMTemplateSet $template_set) {
		$objects = array();
		foreach ($this->getAllFavorites() as $obj) {
			if ($template_set
					->isAuthorizedToViewCollection($obj->getCollection())) {
				$objects[] = $obj;
			}
		}
		return $objects;
	}

	/**
	 * Returns an array of all DMObjects in the user's favorites, whether or
	 * not they are accessible within the current template set.
	 *
	 * @return Array of DMObjects
	 * @see getAccessibleFavorites()
	 */
	public function getAllFavorites() {
		return $this->favorites;
	}

	/**
	 * Deletes multiple DMObjects by removing them from the user's cookie and
	 * then destroying them. Must be called before the HTTP header has
	 * been output.
	 *
	 * @param array objects Array of DMObjects
	 * @return void
	 */
	public function deleteFavorites(array $objects) {
		if (array_key_exists("BUF", $_COOKIE)) {
			$buf = $_COOKIE['BUF'];
			foreach ($objects as &$obj) {
				// delete it from the cookie
				$buf = str_replace(
					sprintf("%s<%d>",
							$obj->getCollection()->getAlias(),
							$obj->getPtr()),
						"", $buf);
				// delete it from the instance
				for ($i = 0; $i < count($this->favorites); $i++) {
					if ($obj->equals($this->favorites[$i])) {
						unset($this->favorites[$i]);
						break;
					}
				}
				// delete it from the passed-in array
				$obj = null;
			}
			if (!headers_sent()) {
				setcookie("BUF", $buf, null, "/");
			}
		}
	}

	/**
	 * The favorites cookie string looks like this: /alias<ptr>/alias<ptr>
	 */
	private function populateFavorites() {
		if (count($this->getAllFavorites()) < 1) {
			if (array_key_exists('BUF', $_COOKIE)) {
				$pairs = explode('>', $_COOKIE['BUF']);
				array_pop($pairs);
				foreach ($pairs as $pair) {
					$tmp = explode('<', $pair);
					$this->favorites[] = DMObjectFactory::getObject(
							DMCollectionFactory::getCollection(
									DMCollection::getSanitizedAlias($tmp[0])),
							$tmp[1]);
				}
			}
		}
	}

	/**
	 * Note: If accessing the flash from the templates, using
	 * DMGenericTemplateHelper::getHtmlFormattedFlash() is preferred.
	 *
	 * @return DMFlash, or null if it has not been set.
	 * @see getFlash()
	 * @see unsetFlash()
	 * @see DMGenericTemplateHelper::isFlash()
	 * @see DMGenericTemplateHelper::drawFormattedFlash()
	 * @since 0.1
	 */
	public function getFlash() {
		if (array_key_exists('flash', $_SESSION)) {
			return ($_SESSION['flash'] instanceof DMFlash)
				? $_SESSION['flash'] : null;
		}
	}

	/**
	 * @param DMFlash flash
	 * @return void
	 * @see unsetFlash()
	 * @see getFlash()
	 * @since 0.1
	 */
	public function setFlash(DMFlash $flash) {
		$_SESSION['flash'] = $flash;
	}

	/**
	 * @return void
	 * @see getFlash()
	 * @see setFlash()
	 * @see DMGenericTemplateHelper::isFlash()
	 * @see DMGenericTemplateHelper::flash()
	 * @since 0.1
	 */
	public function unsetFlash() {
		$_SESSION['flash'] = null;
	}

	/**
	 * @param DMObject obj
	 * @return Boolean
	 * @since 0.1
	 */
	public function hasCommentedOnObject(DMObject $obj) {
		if (!array_key_exists('has_commented', $_SESSION)) {
			return false;
		}
		if (!is_array($_SESSION['has_commented'])) {
			return false;
		}
		foreach ($_SESSION['has_commented'] as $arr) {
			if ($arr['alias'] == $obj->getCollection()->getAlias()
					&& $arr['ptr'] == $obj->getPtr()) {
				return true;
			}
		}
		return false;
	}

	/**
	 * @param DMObject obj
	 * @return void
	 * @since 0.1
	 */
	public function addHasCommented(DMObject $obj) {
		if (self::hasCommentedOnObject($obj)) {
			return;
		}
		if (!array_key_exists('has_commented', $_SESSION)
				|| !is_array($_SESSION['has_commented'])) {
			$_SESSION['has_commented'] = array();
		}
		// if $obj is a child object, add all siblings as well as the parent
		if ($obj->isChild()) {
			$objects = $obj->getParent()->getChildren();
			$objects[] = $obj->getParent();
		} else if ($obj->isCompound()) {
			// if it's a compound object, add all children
			$objects = $obj->getChildren();
			$objects[] = $obj;
		} else { // it's a lonely DMObject
			$objects = array($obj);
		}
		foreach ($objects as $o) {
			$_SESSION['has_commented'][] = array(
				'alias' => $o->getCollection()->getAlias(),
				'ptr' => $o->getPtr()
			);
		}
	}

	/**
	 * @return Array of all alias/ptr pairs that have been rated
	 * @since 0.1
	 */
	public function hasRated() {
		return (array_key_exists("has_rated", $_SESSION)
				&& is_array($_SESSION['has_rated']))
				? $_SESSION['has_rated'] : array();
	}

	/**
	 * @param DMObject obj
	 * @since 0.1
	 */
	public function hasRatedObject(DMObject $obj) {
		if (array_key_exists('has_rated', $_SESSION)) {
			if (is_array($_SESSION['has_rated'])) {
				foreach ($_SESSION['has_rated'] as $arr) {
					if ($arr['alias'] == $obj->getCollection()->getAlias()
					&& $arr['ptr'] == $obj->getPtr()) {
						return true;
					}
				}
			}
		}
		return false;
	}

	/**
	 * @param DMObject obj
	 * @since 0.1
	 */
	public function addHasRated(DMObject $obj) {
		if (!self::hasRatedObject($obj)) {
			if (!array_key_exists("has_rated", $_SESSION)
			|| !is_array($_SESSION['has_rated'])) {
				$_SESSION['has_rated'] = array();
			}
			$_SESSION['has_rated'][] = array(
				'alias' => $obj->getCollection()->getAlias(),
				'ptr' => $obj->getPtr()
			);
		}
	}

	/**
	 * @return DMQuery, or null if nonexistent
	 * @since 0.1
	 */
	public function getQuery() {
		return (array_key_exists('query', $_SESSION)
				&& $_SESSION['query'] instanceof DMQuery)
				? $_SESSION['query'] : null;
	}

	/**
	 * @param DMQuery q
	 * @return void
	 * @since 0.1
	 */
	public function setQuery(DMQuery $q) {
		$_SESSION['query'] = $q;
	}

	/**
	 * @return void
	 * @since 0.1
	 */
	public function unsetQuery() {
		$_SESSION['query'] = null;
	}

	/**
	 * Adds an object to the stack of recently viewed objects. If the given
	 * object is the same as the last object in the stack, it is not added.
	 *
	 * @param DMObject obj
	 */
	public function addRecentlyViewedObject(DMObject $obj) {
		$k = "recently_viewed_objects";
		if (!array_key_exists($k, $_SESSION) || !is_array($_SESSION[$k])) {
			$_SESSION[$k] = array();
		}
		if (count($_SESSION[$k]) > 0
				&& $obj->equals($_SESSION[$k][count($_SESSION[$k]) - 1])) {
			return;
		}
		$_SESSION[$k][] = $obj;
	}

	/**
	 * @param int limit
	 * @return array Array of DMObjects, sorted in chronological order. No
	 * duplicate objects will be returned.
	 */
	public function getRecentlyViewedObjects($limit) {
		$k = "recently_viewed_objects";
		$objects = array();
		if (array_key_exists($k, $_SESSION) && is_array($_SESSION[$k])) {
			foreach ($_SESSION[$k] as $obj) {
				if (count($objects) >= $limit) {
					break;
				}
				foreach ($objects as $obj2) {
					if ($obj2->equals($obj)) {
						continue(2);
					}
				}
				$objects[] = $obj;
			}
		}
		return $objects;
	}

	/**
	 * @return DMResultsView
	 * @since 2.0
	 */
	public function getResultsView() {
		return array_key_exists("results_view", $_SESSION)
			? $_SESSION['results_view'] : null;
	}

	/**
	 * @param string url
	 * @return void
	 * @since 2.0
	 */
	public function setResultsView(DMResultsView $view) {
		$_SESSION['results_view'] = $view;
	}

	/**
	 * @return void
	 * @since 2.0
	 */
	public function unsetResultsView() {
		$_SESSION['results_view'] = null;
	}

	/**
	 * @return DMInput, or null if nonexistent
	 * @since 0.3
	 */
	public function getSearchInput() {
		return (array_key_exists("search_input", $_SESSION))
			? $_SESSION['search_input'] : null;
	}

	/**
	 * @param DMInput input
	 * @return void
	 * @since 0.3
	 */
	public function setSearchInput(DMInput $input) {
		$_SESSION['search_input'] = $input;
	}

	/**
	 * @return void
	 * @since 0.3
	 */
	public function unsetSearchInput() {
		$_SESSION['search_input'] = null;
	}

	/**
	 * @return DMUser object, or null if nonexistent
	 * @since 0.1
	 */
	public function getUser() {
		return (array_key_exists("user", $_SESSION))
				? $_SESSION['user'] : null;
	}

	/**
	 * @param DMUser user
	 * @since 0.1
	 */
	public function setUser(DMUser $user) {
		$_SESSION['user'] = $user;
	}

	/**
	 * @since 0.1
	 */
	public function unsetUser() {
		$_SESSION['user'] = null;
	}

}

